Code Coverage
 
Classes and Traits
Functions and Methods
Lines
Total
0.00% covered (danger)
0.00%
0 / 1
54.55% covered (warning)
54.55%
12 / 22
CRAP
95.97% covered (success)
95.97%
1071 / 1116
PptCharts
0.00% covered (danger)
0.00%
0 / 1
54.55% covered (warning)
54.55%
12 / 22
198
95.97% covered (success)
95.97%
1071 / 1116
 render
0.00% covered (danger)
0.00%
0 / 1
5.02
90.91% covered (success)
90.91%
10 / 11
 writeChart
0.00% covered (danger)
0.00%
0 / 1
8.26
84.06% covered (warning)
84.06%
58 / 69
 writeSpreadsheet
0.00% covered (danger)
0.00%
0 / 1
7.04
90.32% covered (success)
90.32%
28 / 31
 writeElementWithValAttribute
100.00% covered (success)
100.00%
1 / 1
1
100.00% covered (success)
100.00%
4 / 4
 writeSingleValueOrReference
100.00% covered (success)
100.00%
1 / 1
2
100.00% covered (success)
100.00%
16 / 16
 writeMultipleValuesOrReference
100.00% covered (success)
100.00%
1 / 1
6
100.00% covered (success)
100.00%
26 / 26
 writeTitle
100.00% covered (success)
100.00%
1 / 1
4
100.00% covered (success)
100.00%
48 / 48
 writePlotArea
100.00% covered (success)
100.00%
1 / 1
11
100.00% covered (success)
100.00%
26 / 26
 writeLegend
100.00% covered (success)
100.00%
1 / 1
5
100.00% covered (success)
100.00%
48 / 48
 writeLayout
0.00% covered (danger)
0.00%
0 / 1
5.27
77.78% covered (warning)
77.78%
21 / 27
 writeTypeArea
0.00% covered (danger)
0.00%
0 / 1
9.02
93.48% covered (success)
93.48%
43 / 46
 writeTypeBar
0.00% covered (danger)
0.00%
0 / 1
20.25
91.43% covered (success)
91.43%
96 / 105
 writeTypeBar3D
0.00% covered (danger)
0.00%
0 / 1
15
96.74% covered (success)
96.74%
89 / 92
 writeTypeDoughnut
0.00% covered (danger)
0.00%
0 / 1
20
95.29% covered (success)
95.29%
81 / 85
 writeTypePie
0.00% covered (danger)
0.00%
0 / 1
15.03
94.94% covered (success)
94.94%
75 / 79
 writeTypePie3D
100.00% covered (success)
100.00%
1 / 1
13
100.00% covered (success)
100.00%
76 / 76
 writeTypeLine
100.00% covered (success)
100.00%
1 / 1
12
100.00% covered (success)
100.00%
81 / 81
 writeTypeScatter
100.00% covered (success)
100.00%
1 / 1
15
100.00% covered (success)
100.00%
85 / 85
 writeChartRelationships
100.00% covered (success)
100.00%
1 / 1
2
100.00% covered (success)
100.00%
8 / 8
 writeSeriesMarker
100.00% covered (success)
100.00%
1 / 1
4
100.00% covered (success)
100.00%
19 / 19
 writeAxis
0.00% covered (danger)
0.00%
0 / 1
18
99.23% covered (success)
99.23%
129 / 130
 writeAxisGridlines
100.00% covered (success)
100.00%
1 / 1
1
100.00% covered (success)
100.00%
4 / 4
1<?php
2
3namespace PhpOffice\PhpPresentation\Writer\PowerPoint2007;
4
5use PhpOffice\Common\Adapter\Zip\ZipInterface;
6use PhpOffice\Common\Drawing as CommonDrawing;
7use PhpOffice\Common\XMLWriter;
8use PhpOffice\PhpPresentation\PhpPresentation;
9use PhpOffice\PhpPresentation\Shape\Chart;
10use PhpOffice\PhpPresentation\Shape\Chart\Gridlines;
11use PhpOffice\PhpPresentation\Shape\Chart\Legend;
12use PhpOffice\PhpPresentation\Shape\Chart\PlotArea;
13use PhpOffice\PhpPresentation\Shape\Chart\Title;
14use PhpOffice\PhpPresentation\Shape\Chart\Type\Area;
15use PhpOffice\PhpPresentation\Shape\Chart\Type\Bar;
16use PhpOffice\PhpPresentation\Shape\Chart\Type\Bar3D;
17use PhpOffice\PhpPresentation\Shape\Chart\Type\Doughnut;
18use PhpOffice\PhpPresentation\Shape\Chart\Type\Line;
19use PhpOffice\PhpPresentation\Shape\Chart\Type\Pie;
20use PhpOffice\PhpPresentation\Shape\Chart\Type\Pie3D;
21use PhpOffice\PhpPresentation\Shape\Chart\Type\Scatter;
22use PhpOffice\PhpPresentation\Style\Border;
23use PhpOffice\PhpPresentation\Style\Fill;
24use PhpOffice\PhpSpreadsheet\Cell\Coordinate;
25use PhpOffice\PhpSpreadsheet\IOFactory;
26use PhpOffice\PhpSpreadsheet\Spreadsheet;
27
28class PptCharts extends AbstractDecoratorWriter
29{
30    /**
31     * @throws \Exception
32     */
33    public function render(): ZipInterface
34    {
35        for ($i = 0; $i < $this->getDrawingHashTable()->count(); ++$i) {
36            $shape = $this->getDrawingHashTable()->getByIndex($i);
37            if ($shape instanceof Chart) {
38                $this->getZip()->addFromString('ppt/charts/' . $shape->getIndexedFilename(), $this->writeChart($shape));
39
40                if ($shape->hasIncludedSpreadsheet()) {
41                    $this->getZip()->addFromString('ppt/charts/_rels/' . $shape->getIndexedFilename() . '.rels', $this->writeChartRelationships($shape));
42                    $pFilename = tempnam(sys_get_temp_dir(), 'PhpSpreadsheet');
43                    $this->getZip()->addFromString('ppt/embeddings/' . $shape->getIndexedFilename() . '.xlsx', $this->writeSpreadsheet($this->getPresentation(), $shape, $pFilename . '.xlsx'));
44
45                    // remove temp file
46                    if (false === @unlink($pFilename)) {
47                        throw new \Exception('The file ' . $pFilename . ' could not removed.');
48                    }
49                }
50            }
51        }
52
53        return $this->getZip();
54    }
55
56    /**
57     * Write chart to XML format.
58     *
59     * @return string XML Output
60     *
61     * @throws \Exception
62     */
63    public function writeChart(Chart $chart): string
64    {
65        // Create XML writer
66        $objWriter = new XMLWriter(XMLWriter::STORAGE_MEMORY);
67
68        // XML header
69        $objWriter->startDocument('1.0', 'UTF-8', 'yes');
70
71        // c:chartSpace
72        $objWriter->startElement('c:chartSpace');
73        $objWriter->writeAttribute('xmlns:c', 'http://schemas.openxmlformats.org/drawingml/2006/chart');
74        $objWriter->writeAttribute('xmlns:a', 'http://schemas.openxmlformats.org/drawingml/2006/main');
75        $objWriter->writeAttribute('xmlns:r', 'http://schemas.openxmlformats.org/officeDocument/2006/relationships');
76
77        // c:date1904
78        $objWriter->startElement('c:date1904');
79        $objWriter->writeAttribute('val', '1');
80        $objWriter->endElement();
81
82        // c:lang
83        $objWriter->startElement('c:lang');
84        $objWriter->writeAttribute('val', 'en-US');
85        $objWriter->endElement();
86
87        // c:chart
88        $objWriter->startElement('c:chart');
89
90        // Title?
91        if ($chart->getTitle()->isVisible()) {
92            // Write title
93            $this->writeTitle($objWriter, $chart->getTitle());
94        }
95
96        // c:autoTitleDeleted
97        $objWriter->startElement('c:autoTitleDeleted');
98        $objWriter->writeAttribute('val', $chart->getTitle()->isVisible() ? '0' : '1');
99        $objWriter->endElement();
100
101        // c:view3D
102        $objWriter->startElement('c:view3D');
103
104        // c:rotX
105        $objWriter->startElement('c:rotX');
106        $objWriter->writeAttribute('val', $chart->getView3D()->getRotationX());
107        $objWriter->endElement();
108
109        // c:hPercent
110        $hPercent = $chart->getView3D()->getHeightPercent();
111        $objWriter->writeElementIf(null != $hPercent, 'c:hPercent', 'val', $hPercent);
112
113        // c:rotY
114        $objWriter->startElement('c:rotY');
115        $objWriter->writeAttribute('val', $chart->getView3D()->getRotationY());
116        $objWriter->endElement();
117
118        // c:depthPercent
119        $objWriter->startElement('c:depthPercent');
120        $objWriter->writeAttribute('val', $chart->getView3D()->getDepthPercent());
121        $objWriter->endElement();
122
123        // c:rAngAx
124        $objWriter->startElement('c:rAngAx');
125        $objWriter->writeAttribute('val', $chart->getView3D()->hasRightAngleAxes() ? '1' : '0');
126        $objWriter->endElement();
127
128        // c:perspective
129        $objWriter->startElement('c:perspective');
130        $objWriter->writeAttribute('val', $chart->getView3D()->getPerspective());
131        $objWriter->endElement();
132
133        $objWriter->endElement();
134
135        // Write plot area
136        $this->writePlotArea($objWriter, $chart->getPlotArea(), $chart);
137
138        // Legend?
139        if ($chart->getLegend()->isVisible()) {
140            // Write legend
141            $this->writeLegend($objWriter, $chart->getLegend());
142        }
143
144        // c:plotVisOnly
145        $objWriter->startElement('c:plotVisOnly');
146        $objWriter->writeAttribute('val', '1');
147        $objWriter->endElement();
148
149        $objWriter->endElement();
150
151        // c:spPr
152        $objWriter->startElement('c:spPr');
153
154        // Fill
155        $this->writeFill($objWriter, $chart->getFill());
156
157        // Border
158        if (Border::LINE_NONE != $chart->getBorder()->getLineStyle()) {
159            $this->writeBorder($objWriter, $chart->getBorder(), '');
160        }
161
162        // Shadow
163        if ($chart->getShadow()->isVisible()) {
164            // a:effectLst
165            $objWriter->startElement('a:effectLst');
166
167            // a:outerShdw
168            $objWriter->startElement('a:outerShdw');
169            $objWriter->writeAttribute('blurRad', CommonDrawing::pixelsToEmu($chart->getShadow()->getBlurRadius()));
170            $objWriter->writeAttribute('dist', CommonDrawing::pixelsToEmu($chart->getShadow()->getDistance()));
171            $objWriter->writeAttribute('dir', CommonDrawing::degreesToAngle($chart->getShadow()->getDirection()));
172            $objWriter->writeAttribute('algn', $chart->getShadow()->getAlignment());
173            $objWriter->writeAttribute('rotWithShape', '0');
174
175            $this->writeColor($objWriter, $chart->getShadow()->getColor(), $chart->getShadow()->getAlpha());
176
177            $objWriter->endElement();
178
179            $objWriter->endElement();
180        }
181
182        $objWriter->endElement();
183
184        // External data?
185        if ($chart->hasIncludedSpreadsheet()) {
186            // c:externalData
187            $objWriter->startElement('c:externalData');
188            $objWriter->writeAttribute('r:id', 'rId1');
189
190            // c:autoUpdate
191            $objWriter->startElement('c:autoUpdate');
192            $objWriter->writeAttribute('val', '0');
193            $objWriter->endElement();
194
195            $objWriter->endElement();
196        }
197
198        $objWriter->endElement();
199
200        // Return
201        return $objWriter->getData();
202    }
203
204    /**
205     * Write chart to XML format.
206     *
207     * @return string String output
208     *
209     * @throws \Exception
210     */
211    public function writeSpreadsheet(PhpPresentation $presentation, Chart $chart, string $tempName): string
212    {
213        // Need output?
214        if (!$chart->hasIncludedSpreadsheet()) {
215            throw new \Exception('No spreadsheet output is required for the given chart.');
216        }
217
218        // Create new spreadsheet
219        $spreadsheet = new Spreadsheet();
220
221        // Set properties
222        $title = $chart->getTitle()->getText();
223        if (0 == strlen($title)) {
224            $title = 'Chart';
225        }
226        $spreadsheet->getProperties()
227            ->setCreator(
228                $presentation->getDocumentProperties()->getCreator())->setLastModifiedBy(
229                    $presentation->getDocumentProperties()->getLastModifiedBy()
230                )
231            ->setTitle($title);
232
233        // Add chart data
234        $sheet = $spreadsheet->setActiveSheetIndex(0);
235        $sheet->setTitle('Sheet1');
236
237        // Write series
238        $seriesIndex = 0;
239        foreach ($chart->getPlotArea()->getType()->getSeries() as $series) {
240            // Title
241            $sheet->setCellValueByColumnAndRow(1 + $seriesIndex, 1, $series->getTitle());
242
243            // X-axis
244            $axisXData = array_keys($series->getValues());
245            $numAxisXData = count($axisXData);
246            for ($i = 0; $i < $numAxisXData; ++$i) {
247                $sheet->setCellValueByColumnAndRow(0, $i + 2, $axisXData[$i]);
248            }
249
250            // Y-axis
251            $axisYData = array_values($series->getValues());
252            $numAxisYData = count($axisYData);
253            for ($i = 0; $i < $numAxisYData; ++$i) {
254                $sheet->setCellValueByColumnAndRow(1 + $seriesIndex, $i + 2, $axisYData[$i]);
255            }
256
257            ++$seriesIndex;
258        }
259
260        // Save to string
261        $writer = IOFactory::createWriter($spreadsheet, 'Xlsx');
262        $writer->save($tempName);
263
264        // Load file in memory
265        $returnValue = file_get_contents($tempName);
266        if (false === @unlink($tempName)) {
267            throw new \Exception('The file ' . $tempName . ' could not removed.');
268        }
269
270        return $returnValue;
271    }
272
273    /**
274     * Write element with value attribute.
275     *
276     * @param XMLWriter $objWriter XML Writer
277     */
278    protected function writeElementWithValAttribute(XMLWriter $objWriter, string $elementName, string $value): void
279    {
280        $objWriter->startElement($elementName);
281        $objWriter->writeAttribute('val', $value);
282        $objWriter->endElement();
283    }
284
285    /**
286     * Write single value or reference.
287     *
288     * @param XMLWriter $objWriter XML Writer
289     */
290    protected function writeSingleValueOrReference(XMLWriter $objWriter, bool $isReference, string $value, string $reference): void
291    {
292        if (!$isReference) {
293            // Value
294            $objWriter->writeElement('c:v', $value);
295
296            return;
297        }
298
299        // Reference and cache
300        // c:strRef
301        $objWriter->startElement('c:strRef');
302        // c:strRef/c:f
303        $objWriter->writeElement('c:f', $reference);
304        // c:strRef/c:strCache
305        $objWriter->startElement('c:strCache');
306        // c:strRef/c:strCache/c:ptCount
307        $objWriter->startElement('c:ptCount');
308        $objWriter->writeAttribute('val', '1');
309        $objWriter->endElement();
310
311        // c:strRef/c:strCache/c:pt
312        $objWriter->startElement('c:pt');
313        $objWriter->writeAttribute('idx', '0');
314        // c:strRef/c:strCache/c:pt/c:v
315        $objWriter->writeElement('c:v', $value);
316        // c:strRef/c:strCache/c:pt
317        $objWriter->endElement();
318        // c:strRef/c:strCache
319        $objWriter->endElement();
320        // c:strRef
321        $objWriter->endElement();
322    }
323
324    /**
325     * Write series value or reference.
326     *
327     * @param XMLWriter $objWriter XML Writer
328     * @param array<int, mixed> $values
329     */
330    protected function writeMultipleValuesOrReference(XMLWriter $objWriter, bool $isReference, array $values, string $reference): void
331    {
332        // c:strLit / c:numLit
333        // c:strRef / c:numRef
334        $referenceType = ($isReference ? 'Ref' : 'Lit');
335        $dataType = is_numeric($values[0]) ? 'num' : 'str';
336        $objWriter->startElement('c:' . $dataType . $referenceType);
337
338        $numValues = count($values);
339        if (!$isReference) {
340            // Value
341
342            // c:ptCount
343            $objWriter->startElement('c:ptCount');
344            $objWriter->writeAttribute('val', count($values));
345            $objWriter->endElement();
346
347            // Add points
348            for ($i = 0; $i < $numValues; ++$i) {
349                // c:pt
350                $objWriter->startElement('c:pt');
351                $objWriter->writeAttribute('idx', $i);
352                $objWriter->writeElement('c:v', $values[$i]);
353                $objWriter->endElement();
354            }
355        } else {
356            // Reference
357            $objWriter->writeElement('c:f', $reference);
358            $objWriter->startElement('c:' . $dataType . 'Cache');
359
360            // c:ptCount
361            $objWriter->startElement('c:ptCount');
362            $objWriter->writeAttribute('val', count($values));
363            $objWriter->endElement();
364
365            // Add points
366            for ($i = 0; $i < $numValues; ++$i) {
367                // c:pt
368                $objWriter->startElement('c:pt');
369                $objWriter->writeAttribute('idx', $i);
370                $objWriter->writeElement('c:v', $values[$i]);
371                $objWriter->endElement();
372            }
373
374            $objWriter->endElement();
375        }
376
377        $objWriter->endElement();
378    }
379
380    /**
381     * Write Title.
382     *
383     * @param XMLWriter $objWriter XML Writer
384     *
385     * @throws \Exception
386     */
387    protected function writeTitle(XMLWriter $objWriter, Title $subject): void
388    {
389        // c:title
390        $objWriter->startElement('c:title');
391
392        // c:tx
393        $objWriter->startElement('c:tx');
394
395        // c:rich
396        $objWriter->startElement('c:rich');
397
398        // a:bodyPr
399        $objWriter->writeElement('a:bodyPr', null);
400
401        // a:lstStyle
402        $objWriter->writeElement('a:lstStyle', null);
403
404        // a:p
405        $objWriter->startElement('a:p');
406
407        // a:pPr
408        $objWriter->startElement('a:pPr');
409        $objWriter->writeAttribute('algn', $subject->getAlignment()->getHorizontal());
410        $objWriter->writeAttribute('fontAlgn', $subject->getAlignment()->getVertical());
411        $objWriter->writeAttribute('marL', CommonDrawing::pixelsToEmu($subject->getAlignment()->getMarginLeft()));
412        $objWriter->writeAttribute('marR', CommonDrawing::pixelsToEmu($subject->getAlignment()->getMarginRight()));
413        $objWriter->writeAttribute('indent', CommonDrawing::pixelsToEmu($subject->getAlignment()->getIndent()));
414        $objWriter->writeAttribute('lvl', $subject->getAlignment()->getLevel());
415
416        // a:defRPr
417        $objWriter->writeElement('a:defRPr', null);
418
419        $objWriter->endElement();
420
421        // a:r
422        $objWriter->startElement('a:r');
423
424        // a:rPr
425        $objWriter->startElement('a:rPr');
426        $objWriter->writeAttribute('lang', 'en-US');
427        $objWriter->writeAttribute('dirty', '0');
428        $objWriter->writeAttribute('b', ($subject->getFont()->isBold() ? 'true' : 'false'));
429        $objWriter->writeAttribute('i', ($subject->getFont()->isItalic() ? 'true' : 'false'));
430        $objWriter->writeAttribute('strike', ($subject->getFont()->isStrikethrough() ? 'sngStrike' : 'noStrike'));
431        $objWriter->writeAttribute('sz', ($subject->getFont()->getSize() * 100));
432        $objWriter->writeAttribute('u', $subject->getFont()->getUnderline());
433        $objWriter->writeAttributeIf($subject->getFont()->isSuperScript(), 'baseline', '300000');
434        $objWriter->writeAttributeIf($subject->getFont()->isSubScript(), 'baseline', '-250000');
435
436        // Font - a:solidFill
437        $objWriter->startElement('a:solidFill');
438
439        $this->writeColor($objWriter, $subject->getFont()->getColor());
440
441        $objWriter->endElement();
442
443        // Font - a:latin
444        $objWriter->startElement('a:latin');
445        $objWriter->writeAttribute('typeface', $subject->getFont()->getName());
446        $objWriter->endElement();
447
448        $objWriter->endElement();
449
450        // a:t
451        $objWriter->writeElement('a:t', $subject->getText());
452
453        $objWriter->endElement();
454
455        // a:endParaRPr
456        $objWriter->startElement('a:endParaRPr');
457        $objWriter->writeAttribute('lang', 'en-US');
458        $objWriter->writeAttribute('dirty', '0');
459        $objWriter->endElement();
460
461        $objWriter->endElement();
462
463        $objWriter->endElement();
464
465        $objWriter->endElement();
466
467        // Write layout
468        $this->writeLayout($objWriter, $subject);
469
470        // c:overlay
471        $objWriter->startElement('c:overlay');
472        $objWriter->writeAttribute('val', '0');
473        $objWriter->endElement();
474
475        $objWriter->endElement();
476    }
477
478    /**
479     * Write Plot Area.
480     *
481     * @param XMLWriter $objWriter XML Writer
482     *
483     * @throws \Exception
484     */
485    protected function writePlotArea(XMLWriter $objWriter, PlotArea $subject, Chart $chart): void
486    {
487        // c:plotArea
488        $objWriter->startElement('c:plotArea');
489
490        // Write layout
491        $this->writeLayout($objWriter, $subject);
492
493        // Write chart
494        $chartType = $subject->getType();
495        if ($chartType instanceof Area) {
496            $this->writeTypeArea($objWriter, $chartType, $chart->hasIncludedSpreadsheet());
497        } elseif ($chartType instanceof Bar) {
498            $this->writeTypeBar($objWriter, $chartType, $chart->hasIncludedSpreadsheet());
499        } elseif ($chartType instanceof Bar3D) {
500            $this->writeTypeBar3D($objWriter, $chartType, $chart->hasIncludedSpreadsheet());
501        } elseif ($chartType instanceof Doughnut) {
502            $this->writeTypeDoughnut($objWriter, $chartType, $chart->hasIncludedSpreadsheet());
503        } elseif ($chartType instanceof Pie) {
504            $this->writeTypePie($objWriter, $chartType, $chart->hasIncludedSpreadsheet());
505        } elseif ($chartType instanceof Pie3D) {
506            $this->writeTypePie3D($objWriter, $chartType, $chart->hasIncludedSpreadsheet());
507        } elseif ($chartType instanceof Line) {
508            $this->writeTypeLine($objWriter, $chartType, $chart->hasIncludedSpreadsheet());
509        } elseif ($chartType instanceof Scatter) {
510            $this->writeTypeScatter($objWriter, $chartType, $chart->hasIncludedSpreadsheet());
511        } else {
512            throw new \Exception('The chart type provided could not be rendered.');
513        }
514
515        // Write X axis?
516        if ($chartType->hasAxisX()) {
517            $this->writeAxis($objWriter, $subject->getAxisX(), Chart\Axis::AXIS_X, $chartType);
518        }
519
520        // Write Y axis?
521        if ($chartType->hasAxisY()) {
522            $this->writeAxis($objWriter, $subject->getAxisY(), Chart\Axis::AXIS_Y, $chartType);
523        }
524
525        $objWriter->endElement();
526    }
527
528    /**
529     * Write Legend.
530     *
531     * @param XMLWriter $objWriter XML Writer
532     * @param Chart\Legend $subject
533     *
534     * @throws \Exception
535     */
536    protected function writeLegend(XMLWriter $objWriter, Legend $subject): void
537    {
538        // c:legend
539        $objWriter->startElement('c:legend');
540
541        // c:legendPos
542        $objWriter->startElement('c:legendPos');
543        $objWriter->writeAttribute('val', $subject->getPosition());
544        $objWriter->endElement();
545
546        // Write layout
547        $this->writeLayout($objWriter, $subject);
548
549        // c:overlay
550        $objWriter->startElement('c:overlay');
551        $objWriter->writeAttribute('val', '0');
552        $objWriter->endElement();
553
554        // c:spPr
555        $objWriter->startElement('c:spPr');
556
557        // Fill
558        $this->writeFill($objWriter, $subject->getFill());
559
560        // Border
561        if (Border::LINE_NONE != $subject->getBorder()->getLineStyle()) {
562            $this->writeBorder($objWriter, $subject->getBorder(), '');
563        }
564
565        $objWriter->endElement();
566
567        // c:txPr
568        $objWriter->startElement('c:txPr');
569
570        // a:bodyPr
571        $objWriter->writeElement('a:bodyPr', null);
572
573        // a:lstStyle
574        $objWriter->writeElement('a:lstStyle', null);
575
576        // a:p
577        $objWriter->startElement('a:p');
578
579        // a:pPr
580        $objWriter->startElement('a:pPr');
581        $objWriter->writeAttribute('algn', $subject->getAlignment()->getHorizontal());
582        $objWriter->writeAttribute('fontAlgn', $subject->getAlignment()->getVertical());
583        $objWriter->writeAttribute('marL', CommonDrawing::pixelsToEmu($subject->getAlignment()->getMarginLeft()));
584        $objWriter->writeAttribute('marR', CommonDrawing::pixelsToEmu($subject->getAlignment()->getMarginRight()));
585        $objWriter->writeAttribute('indent', CommonDrawing::pixelsToEmu($subject->getAlignment()->getIndent()));
586        $objWriter->writeAttribute('lvl', $subject->getAlignment()->getLevel());
587
588        // a:defRPr
589        $objWriter->startElement('a:defRPr');
590
591        $objWriter->writeAttribute('b', ($subject->getFont()->isBold() ? 'true' : 'false'));
592        $objWriter->writeAttribute('i', ($subject->getFont()->isItalic() ? 'true' : 'false'));
593        $objWriter->writeAttribute('strike', ($subject->getFont()->isStrikethrough() ? 'sngStrike' : 'noStrike'));
594        $objWriter->writeAttribute('sz', ($subject->getFont()->getSize() * 100));
595        $objWriter->writeAttribute('u', $subject->getFont()->getUnderline());
596        $objWriter->writeAttributeIf($subject->getFont()->isSuperScript(), 'baseline', '300000');
597        $objWriter->writeAttributeIf($subject->getFont()->isSubScript(), 'baseline', '-250000');
598
599        // Font - a:solidFill
600        $objWriter->startElement('a:solidFill');
601
602        $this->writeColor($objWriter, $subject->getFont()->getColor());
603
604        $objWriter->endElement();
605
606        // Font - a:latin
607        $objWriter->startElement('a:latin');
608        $objWriter->writeAttribute('typeface', $subject->getFont()->getName());
609        $objWriter->endElement();
610
611        $objWriter->endElement();
612
613        $objWriter->endElement();
614
615        // a:endParaRPr
616        $objWriter->startElement('a:endParaRPr');
617        $objWriter->writeAttribute('lang', 'en-US');
618        $objWriter->writeAttribute('dirty', '0');
619        $objWriter->endElement();
620
621        $objWriter->endElement();
622
623        $objWriter->endElement();
624
625        $objWriter->endElement();
626    }
627
628    /**
629     * Write Layout.
630     *
631     * @param XMLWriter $objWriter XML Writer
632     * @param Legend|PlotArea|Title $subject
633     *
634     * @throws \Exception
635     */
636    protected function writeLayout(XMLWriter $objWriter, $subject): void
637    {
638        // c:layout
639        $objWriter->startElement('c:layout');
640
641        // c:manualLayout
642        $objWriter->startElement('c:manualLayout');
643        // c:xMode
644        $objWriter->startElement('c:xMode');
645        $objWriter->writeAttribute('val', 'edge');
646        $objWriter->endElement();
647
648        // c:yMode
649        $objWriter->startElement('c:yMode');
650        $objWriter->writeAttribute('val', 'edge');
651        $objWriter->endElement();
652
653        if (0 != $subject->getOffsetX()) {
654            // c:x
655            $objWriter->startElement('c:x');
656            $objWriter->writeAttribute('val', $subject->getOffsetX());
657            $objWriter->endElement();
658        }
659
660        if (0 != $subject->getOffsetY()) {
661            // c:y
662            $objWriter->startElement('c:y');
663            $objWriter->writeAttribute('val', $subject->getOffsetY());
664            $objWriter->endElement();
665        }
666
667        if (0 != $subject->getWidth()) {
668            // c:w
669            $objWriter->startElement('c:w');
670            $objWriter->writeAttribute('val', $subject->getWidth());
671            $objWriter->endElement();
672        }
673
674        if (0 != $subject->getHeight()) {
675            // c:h
676            $objWriter->startElement('c:h');
677            $objWriter->writeAttribute('val', $subject->getHeight());
678            $objWriter->endElement();
679        }
680
681        $objWriter->endElement();
682        $objWriter->endElement();
683    }
684
685    /**
686     * Write Type Area.
687     *
688     * @param XMLWriter $objWriter XML Writer
689     * @param Chart\Type\Area $subject
690     *
691     * @throws \Exception
692     */
693    protected function writeTypeArea(XMLWriter $objWriter, Area $subject, bool $includeSheet = false): void
694    {
695        // c:lineChart
696        $objWriter->startElement('c:areaChart');
697
698        // c:grouping
699        $objWriter->startElement('c:grouping');
700        $objWriter->writeAttribute('val', 'standard');
701        $objWriter->endElement();
702
703        // Write series
704        $seriesIndex = 0;
705        foreach ($subject->getSeries() as $series) {
706            // c:ser
707            $objWriter->startElement('c:ser');
708
709            // c:ser > c:idx
710            $objWriter->startElement('c:idx');
711            $objWriter->writeAttribute('val', $seriesIndex);
712            $objWriter->endElement();
713
714            // c:ser > c:order
715            $objWriter->startElement('c:order');
716            $objWriter->writeAttribute('val', $seriesIndex);
717            $objWriter->endElement();
718
719            // c:ser > c:tx
720            $objWriter->startElement('c:tx');
721            $coords = ($includeSheet ? 'Sheet1!$' . Coordinate::stringFromColumnIndex(1 + $seriesIndex) . '$1' : '');
722            $this->writeSingleValueOrReference($objWriter, $includeSheet, $series->getTitle(), $coords);
723            $objWriter->endElement();
724
725            // c:ser > c:dLbls
726            // @link : https://msdn.microsoft.com/en-us/library/documentformat.openxml.drawing.charts.areachartseries.aspx
727            $objWriter->startElement('c:dLbls');
728
729            // c:ser > c:dLbls > c:showVal
730            $this->writeElementWithValAttribute($objWriter, 'c:showVal', $series->hasShowValue() ? '1' : '0');
731
732            // c:ser > c:dLbls > c:showCatName
733            $this->writeElementWithValAttribute($objWriter, 'c:showCatName', $series->hasShowCategoryName() ? '1' : '0');
734
735            // c:ser > c:dLbls > c:showSerName
736            $this->writeElementWithValAttribute($objWriter, 'c:showSerName', $series->hasShowSeriesName() ? '1' : '0');
737
738            // c:ser > c:dLbls > c:showPercent
739            $this->writeElementWithValAttribute($objWriter, 'c:showPercent', $series->hasShowPercentage() ? '1' : '0');
740
741            // c:ser > ##c:dLbls
742            $objWriter->endElement();
743
744            if (Fill::FILL_NONE != $series->getFill()->getFillType()) {
745                // c:spPr
746                $objWriter->startElement('c:spPr');
747                // Write fill
748                $this->writeFill($objWriter, $series->getFill());
749                // ## c:spPr
750                $objWriter->endElement();
751            }
752
753            // Write X axis data
754            $axisXData = array_keys($series->getValues());
755
756            // c:cat
757            $objWriter->startElement('c:cat');
758            $this->writeMultipleValuesOrReference($objWriter, $includeSheet, $axisXData, 'Sheet1!$A$2:$A$' . (1 + count($axisXData)));
759            $objWriter->endElement();
760
761            // Write Y axis data
762            $axisYData = array_values($series->getValues());
763
764            // c:val
765            $objWriter->startElement('c:val');
766            $coords = ($includeSheet ? 'Sheet1!$' . Coordinate::stringFromColumnIndex($seriesIndex + 1) . '$2:$' . Coordinate::stringFromColumnIndex($seriesIndex + 1) . '$' . (1 + count($axisYData)) : '');
767            $this->writeMultipleValuesOrReference($objWriter, $includeSheet, $axisYData, $coords);
768            $objWriter->endElement();
769
770            $objWriter->endElement();
771
772            ++$seriesIndex;
773        }
774
775        // c:axId
776        $objWriter->startElement('c:axId');
777        $objWriter->writeAttribute('val', '52743552');
778        $objWriter->endElement();
779
780        // c:axId
781        $objWriter->startElement('c:axId');
782        $objWriter->writeAttribute('val', '52749440');
783        $objWriter->endElement();
784
785        $objWriter->endElement();
786    }
787
788    /**
789     * Write Type Bar.
790     *
791     * @param XMLWriter $objWriter XML Writer
792     * @param Chart\Type\Bar $subject
793     *
794     * @throws \Exception
795     */
796    protected function writeTypeBar(XMLWriter $objWriter, Bar $subject, bool $includeSheet = false): void
797    {
798        // c:barChart
799        $objWriter->startElement('c:barChart');
800
801        // c:barDir
802        $objWriter->startElement('c:barDir');
803        $objWriter->writeAttribute('val', $subject->getBarDirection());
804        $objWriter->endElement();
805
806        // c:grouping
807        $objWriter->startElement('c:grouping');
808        $objWriter->writeAttribute('val', $subject->getBarGrouping());
809        $objWriter->endElement();
810
811        // Write series
812        $seriesIndex = 0;
813        foreach ($subject->getSeries() as $series) {
814            // c:ser
815            $objWriter->startElement('c:ser');
816
817            // c:idx
818            $objWriter->startElement('c:idx');
819            $objWriter->writeAttribute('val', $seriesIndex);
820            $objWriter->endElement();
821
822            // c:order
823            $objWriter->startElement('c:order');
824            $objWriter->writeAttribute('val', $seriesIndex);
825            $objWriter->endElement();
826
827            // c:tx
828            $objWriter->startElement('c:tx');
829            $coords = ($includeSheet ? 'Sheet1!$' . Coordinate::stringFromColumnIndex(1 + $seriesIndex) . '$1' : '');
830            $this->writeSingleValueOrReference($objWriter, $includeSheet, $series->getTitle(), $coords);
831            $objWriter->endElement();
832
833            // Fills for points?
834            $dataPointFills = $series->getDataPointFills();
835            foreach ($dataPointFills as $key => $value) {
836                // c:dPt
837                $objWriter->startElement('c:dPt');
838
839                // c:idx
840                $this->writeElementWithValAttribute($objWriter, 'c:idx', $key);
841
842                if (Fill::FILL_NONE != $value->getFillType()) {
843                    // c:spPr
844                    $objWriter->startElement('c:spPr');
845                    // Write fill
846                    $this->writeFill($objWriter, $value);
847                    // ## c:spPr
848                    $objWriter->endElement();
849                }
850
851                // ## c:dPt
852                $objWriter->endElement();
853            }
854
855            // c:dLbls
856            $objWriter->startElement('c:dLbls');
857
858            if ($series->hasDlblNumFormat()) {
859                //c:numFmt
860                $objWriter->startElement('c:numFmt');
861                $objWriter->writeAttribute('formatCode', $series->getDlblNumFormat());
862                $objWriter->writeAttribute('sourceLinked', '0');
863                $objWriter->endElement();
864            }
865
866            // c:txPr
867            $objWriter->startElement('c:txPr');
868
869            // a:bodyPr
870            $objWriter->writeElement('a:bodyPr');
871
872            // a:lstStyle
873            $objWriter->writeElement('a:lstStyle');
874
875            // a:p
876            $objWriter->startElement('a:p');
877
878            // a:pPr
879            $objWriter->startElement('a:pPr');
880
881            // a:defRPr
882            $objWriter->startElement('a:defRPr');
883
884            $objWriter->writeAttribute('b', ($series->getFont()->isBold() ? 'true' : 'false'));
885            $objWriter->writeAttribute('i', ($series->getFont()->isItalic() ? 'true' : 'false'));
886            $objWriter->writeAttribute('strike', ($series->getFont()->isStrikethrough() ? 'sngStrike' : 'noStrike'));
887            $objWriter->writeAttribute('sz', ($series->getFont()->getSize() * 100));
888            $objWriter->writeAttribute('u', $series->getFont()->getUnderline());
889            $objWriter->writeAttributeIf($series->getFont()->isSuperScript(), 'baseline', '300000');
890            $objWriter->writeAttributeIf($series->getFont()->isSubScript(), 'baseline', '-250000');
891
892            // a:solidFill
893            $objWriter->startElement('a:solidFill');
894            $this->writeColor($objWriter, $series->getFont()->getColor());
895            // >a:solidFill
896            $objWriter->endElement();
897            // a:latin
898            $objWriter->startElement('a:latin');
899            $objWriter->writeAttribute('typeface', $series->getFont()->getName());
900            // >a:latin
901            $objWriter->endElement();
902
903            // >a:defRPr
904            $objWriter->endElement();
905            // >a:pPr
906            $objWriter->endElement();
907
908            // a:endParaRPr
909            $objWriter->startElement('a:endParaRPr');
910            $objWriter->writeAttribute('lang', 'en-US');
911            $objWriter->writeAttribute('dirty', '0');
912            $objWriter->endElement();
913
914            // >a:p
915            $objWriter->endElement();
916            // >a:lstStyle
917            $objWriter->endElement();
918
919            // c:dLblPos
920            $this->writeElementWithValAttribute($objWriter, 'c:dLblPos', $series->getLabelPosition());
921
922            // c:showVal
923            $this->writeElementWithValAttribute($objWriter, 'c:showVal', $series->hasShowValue() ? '1' : '0');
924
925            // c:showCatName
926            $this->writeElementWithValAttribute($objWriter, 'c:showCatName', $series->hasShowCategoryName() ? '1' : '0');
927
928            // c:showSerName
929            $this->writeElementWithValAttribute($objWriter, 'c:showSerName', $series->hasShowSeriesName() ? '1' : '0');
930
931            // c:showPercent
932            $this->writeElementWithValAttribute($objWriter, 'c:showPercent', $series->hasShowPercentage() ? '1' : '0');
933
934            // c:separator
935            $objWriter->writeElement('c:separator', $series->hasShowSeparator() ? $series->getSeparator() : '');
936
937            // c:showLeaderLines
938            $this->writeElementWithValAttribute($objWriter, 'c:showLeaderLines', $series->hasShowLeaderLines() ? '1' : '0');
939
940            $objWriter->endElement();
941
942            // c:spPr
943            if (Fill::FILL_NONE != $series->getFill()->getFillType()) {
944                // c:spPr
945                $objWriter->startElement('c:spPr');
946                // Write fill
947                $this->writeFill($objWriter, $series->getFill());
948                // ## c:spPr
949                $objWriter->endElement();
950            }
951
952            // Write X axis data
953            $axisXData = array_keys($series->getValues());
954
955            // c:cat
956            $objWriter->startElement('c:cat');
957            $this->writeMultipleValuesOrReference($objWriter, $includeSheet, $axisXData, 'Sheet1!$A$2:$A$' . (1 + count($axisXData)));
958            $objWriter->endElement();
959
960            // Write Y axis data
961            $axisYData = array_values($series->getValues());
962
963            // c:val
964            $objWriter->startElement('c:val');
965            $coords = ($includeSheet ? 'Sheet1!$' . Coordinate::stringFromColumnIndex($seriesIndex + 1) . '$2:$' . Coordinate::stringFromColumnIndex($seriesIndex + 1) . '$' . (1 + count($axisYData)) : '');
966            $this->writeMultipleValuesOrReference($objWriter, $includeSheet, $axisYData, $coords);
967            $objWriter->endElement();
968
969            $objWriter->endElement();
970
971            ++$seriesIndex;
972        }
973
974        // c:gapWidth
975        $objWriter->startElement('c:gapWidth');
976        $objWriter->writeAttribute('val', $subject->getGapWidthPercent());
977        $objWriter->endElement();
978
979        // c:overlap
980        $barGrouping = $subject->getBarGrouping();
981        $objWriter->startElement('c:overlap');
982        if (Bar::GROUPING_CLUSTERED === $barGrouping) {
983            $objWriter->writeAttribute('val', '0');
984        } elseif (Bar::GROUPING_STACKED === $barGrouping || Bar::GROUPING_PERCENTSTACKED === $barGrouping) {
985            $objWriter->writeAttribute('val', '100');
986        }
987        $objWriter->endElement();
988
989        // c:axId
990        $objWriter->startElement('c:axId');
991        $objWriter->writeAttribute('val', '52743552');
992        $objWriter->endElement();
993
994        // c:axId
995        $objWriter->startElement('c:axId');
996        $objWriter->writeAttribute('val', '52749440');
997        $objWriter->endElement();
998
999        // c:extLst
1000        $objWriter->startElement('c:extLst');
1001        $objWriter->endElement();
1002
1003        $objWriter->endElement();
1004    }
1005
1006    /**
1007     * Write Type Bar3D.
1008     *
1009     * @param XMLWriter $objWriter XML Writer
1010     * @param Chart\Type\Bar3D $subject
1011     *
1012     * @throws \Exception
1013     */
1014    protected function writeTypeBar3D(XMLWriter $objWriter, Bar3D $subject, bool $includeSheet = false): void
1015    {
1016        // c:bar3DChart
1017        $objWriter->startElement('c:bar3DChart');
1018
1019        // c:barDir
1020        $objWriter->startElement('c:barDir');
1021        $objWriter->writeAttribute('val', $subject->getBarDirection());
1022        $objWriter->endElement();
1023
1024        // c:grouping
1025        $objWriter->startElement('c:grouping');
1026        $objWriter->writeAttribute('val', $subject->getBarGrouping());
1027        $objWriter->endElement();
1028
1029        // Write series
1030        $seriesIndex = 0;
1031        foreach ($subject->getSeries() as $series) {
1032            // c:ser
1033            $objWriter->startElement('c:ser');
1034
1035            // c:idx
1036            $objWriter->startElement('c:idx');
1037            $objWriter->writeAttribute('val', $seriesIndex);
1038            $objWriter->endElement();
1039
1040            // c:order
1041            $objWriter->startElement('c:order');
1042            $objWriter->writeAttribute('val', $seriesIndex);
1043            $objWriter->endElement();
1044
1045            // c:tx
1046            $objWriter->startElement('c:tx');
1047            $coords = ($includeSheet ? 'Sheet1!$' . Coordinate::stringFromColumnIndex(1 + $seriesIndex) . '$1' : '');
1048            $this->writeSingleValueOrReference($objWriter, $includeSheet, $series->getTitle(), $coords);
1049            $objWriter->endElement();
1050
1051            // Fills for points?
1052            $dataPointFills = $series->getDataPointFills();
1053            foreach ($dataPointFills as $key => $value) {
1054                // c:dPt
1055                $objWriter->startElement('c:dPt');
1056
1057                // c:idx
1058                $this->writeElementWithValAttribute($objWriter, 'c:idx', $key);
1059
1060                if (Fill::FILL_NONE != $value->getFillType()) {
1061                    // c:spPr
1062                    $objWriter->startElement('c:spPr');
1063                    // Write fill
1064                    $this->writeFill($objWriter, $value);
1065                    // ## c:spPr
1066                    $objWriter->endElement();
1067                }
1068
1069                // ## c:dPt
1070                $objWriter->endElement();
1071            }
1072
1073            // c:dLbls
1074            $objWriter->startElement('c:dLbls');
1075
1076            // c:txPr
1077            $objWriter->startElement('c:txPr');
1078
1079            // a:bodyPr
1080            $objWriter->writeElement('a:bodyPr', null);
1081
1082            // a:lstStyle
1083            $objWriter->writeElement('a:lstStyle', null);
1084
1085            // a:p
1086            $objWriter->startElement('a:p');
1087
1088            // a:pPr
1089            $objWriter->startElement('a:pPr');
1090
1091            // a:defRPr
1092            $objWriter->startElement('a:defRPr');
1093
1094            $objWriter->writeAttribute('b', ($series->getFont()->isBold() ? 'true' : 'false'));
1095            $objWriter->writeAttribute('i', ($series->getFont()->isItalic() ? 'true' : 'false'));
1096            $objWriter->writeAttribute('strike', ($series->getFont()->isStrikethrough() ? 'sngStrike' : 'noStrike'));
1097            $objWriter->writeAttribute('sz', ($series->getFont()->getSize() * 100));
1098            $objWriter->writeAttribute('u', $series->getFont()->getUnderline());
1099            $objWriter->writeAttributeIf($series->getFont()->isSuperScript(), 'baseline', '300000');
1100            $objWriter->writeAttributeIf($series->getFont()->isSubScript(), 'baseline', '-250000');
1101
1102            // Font - a:solidFill
1103            $objWriter->startElement('a:solidFill');
1104
1105            $this->writeColor($objWriter, $series->getFont()->getColor());
1106
1107            $objWriter->endElement();
1108
1109            // Font - a:latin
1110            $objWriter->startElement('a:latin');
1111            $objWriter->writeAttribute('typeface', $series->getFont()->getName());
1112            $objWriter->endElement();
1113
1114            $objWriter->endElement();
1115
1116            $objWriter->endElement();
1117
1118            // a:endParaRPr
1119            $objWriter->startElement('a:endParaRPr');
1120            $objWriter->writeAttribute('lang', 'en-US');
1121            $objWriter->writeAttribute('dirty', '0');
1122            $objWriter->endElement();
1123
1124            $objWriter->endElement();
1125
1126            $objWriter->endElement();
1127
1128            // c:showVal
1129            $this->writeElementWithValAttribute($objWriter, 'c:showVal', $series->hasShowValue() ? '1' : '0');
1130
1131            // c:showCatName
1132            $this->writeElementWithValAttribute($objWriter, 'c:showCatName', $series->hasShowCategoryName() ? '1' : '0');
1133
1134            // c:showSerName
1135            $this->writeElementWithValAttribute($objWriter, 'c:showSerName', $series->hasShowSeriesName() ? '1' : '0');
1136
1137            // c:showPercent
1138            $this->writeElementWithValAttribute($objWriter, 'c:showPercent', $series->hasShowPercentage() ? '1' : '0');
1139
1140            // c:showLeaderLines
1141            $this->writeElementWithValAttribute($objWriter, 'c:showLeaderLines', $series->hasShowLeaderLines() ? '1' : '0');
1142
1143            $objWriter->endElement();
1144
1145            // c:spPr
1146            if (Fill::FILL_NONE != $series->getFill()->getFillType()) {
1147                // c:spPr
1148                $objWriter->startElement('c:spPr');
1149                // Write fill
1150                $this->writeFill($objWriter, $series->getFill());
1151                // ## c:spPr
1152                $objWriter->endElement();
1153            }
1154
1155            // Write X axis data
1156            $axisXData = array_keys($series->getValues());
1157
1158            // c:cat
1159            $objWriter->startElement('c:cat');
1160            $this->writeMultipleValuesOrReference($objWriter, $includeSheet, $axisXData, 'Sheet1!$A$2:$A$' . (1 + count($axisXData)));
1161            $objWriter->endElement();
1162
1163            // Write Y axis data
1164            $axisYData = array_values($series->getValues());
1165
1166            // c:val
1167            $objWriter->startElement('c:val');
1168            $coords = ($includeSheet ? 'Sheet1!$' . Coordinate::stringFromColumnIndex($seriesIndex + 1) . '$2:$' . Coordinate::stringFromColumnIndex($seriesIndex + 1) . '$' . (1 + count($axisYData)) : '');
1169            $this->writeMultipleValuesOrReference($objWriter, $includeSheet, $axisYData, $coords);
1170            $objWriter->endElement();
1171
1172            $objWriter->endElement();
1173
1174            ++$seriesIndex;
1175        }
1176
1177        // c:gapWidth
1178        $objWriter->startElement('c:gapWidth');
1179        $objWriter->writeAttribute('val', $subject->getGapWidthPercent());
1180        $objWriter->endElement();
1181
1182        // c:axId
1183        $objWriter->startElement('c:axId');
1184        $objWriter->writeAttribute('val', '52743552');
1185        $objWriter->endElement();
1186
1187        // c:axId
1188        $objWriter->startElement('c:axId');
1189        $objWriter->writeAttribute('val', '52749440');
1190        $objWriter->endElement();
1191
1192        // c:axId
1193        $objWriter->startElement('c:axId');
1194        $objWriter->writeAttribute('val', '0');
1195        $objWriter->endElement();
1196
1197        $objWriter->endElement();
1198    }
1199
1200    /**
1201     * Write Type Pie.
1202     *
1203     * @param XMLWriter $objWriter XML Writer
1204     *
1205     * @throws \Exception
1206     */
1207    protected function writeTypeDoughnut(XMLWriter $objWriter, Doughnut $subject, bool $includeSheet = false): void
1208    {
1209        // c:pieChart
1210        $objWriter->startElement('c:doughnutChart');
1211
1212        // c:varyColors
1213        $objWriter->startElement('c:varyColors');
1214        $objWriter->writeAttribute('val', '1');
1215        $objWriter->endElement();
1216
1217        // Write series
1218        $seriesIndex = 0;
1219        foreach ($subject->getSeries() as $series) {
1220            // c:ser
1221            $objWriter->startElement('c:ser');
1222
1223            // c:idx
1224            $objWriter->startElement('c:idx');
1225            $objWriter->writeAttribute('val', $seriesIndex);
1226            $objWriter->endElement();
1227
1228            // c:order
1229            $objWriter->startElement('c:order');
1230            $objWriter->writeAttribute('val', $seriesIndex);
1231            $objWriter->endElement();
1232
1233            // c:tx
1234            $objWriter->startElement('c:tx');
1235            $coords = ($includeSheet ? 'Sheet1!$' . Coordinate::stringFromColumnIndex(1 + $seriesIndex) . '$1' : '');
1236            $this->writeSingleValueOrReference($objWriter, $includeSheet, $series->getTitle(), $coords);
1237            $objWriter->endElement();
1238
1239            // Fills for points?
1240            $dataPointFills = $series->getDataPointFills();
1241            foreach ($dataPointFills as $key => $value) {
1242                // c:dPt
1243                $objWriter->startElement('c:dPt');
1244                $this->writeElementWithValAttribute($objWriter, 'c:idx', $key);
1245                // c:dPt/c:spPr
1246                $objWriter->startElement('c:spPr');
1247                $this->writeFill($objWriter, $value);
1248                // c:dPt/##c:spPr
1249                $objWriter->endElement();
1250                // ##c:dPt
1251                $objWriter->endElement();
1252            }
1253
1254            // Write X axis data
1255            $axisXData = array_keys($series->getValues());
1256
1257            // c:cat
1258            $objWriter->startElement('c:cat');
1259            $this->writeMultipleValuesOrReference($objWriter, $includeSheet, $axisXData, 'Sheet1!$A$2:$A$' . (1 + count($axisXData)));
1260            $objWriter->endElement();
1261
1262            // Write Y axis data
1263            $axisYData = array_values($series->getValues());
1264
1265            // c:val
1266            $objWriter->startElement('c:val');
1267            $coords = ($includeSheet ? 'Sheet1!$' . Coordinate::stringFromColumnIndex($seriesIndex + 1) . '$2:$' . Coordinate::stringFromColumnIndex($seriesIndex + 1) . '$' . (1 + count($axisYData)) : '');
1268            $this->writeMultipleValuesOrReference($objWriter, $includeSheet, $axisYData, $coords);
1269            $objWriter->endElement();
1270
1271            $objWriter->endElement();
1272
1273            ++$seriesIndex;
1274        }
1275
1276        if (isset($series) && is_object($series) && $series instanceof Chart\Series) {
1277            // c:dLbls
1278            $objWriter->startElement('c:dLbls');
1279
1280            $this->writeElementWithValAttribute($objWriter, 'c:showLegendKey', $series->hasShowLegendKey() ? '1' : '0');
1281            $this->writeElementWithValAttribute($objWriter, 'c:showVal', $series->hasShowValue() ? '1' : '0');
1282            $this->writeElementWithValAttribute($objWriter, 'c:showCatName', $series->hasShowCategoryName() ? '1' : '0');
1283            $this->writeElementWithValAttribute($objWriter, 'c:showSerName', $series->hasShowSeriesName() ? '1' : '0');
1284            $this->writeElementWithValAttribute($objWriter, 'c:showPercent', $series->hasShowPercentage() ? '1' : '0');
1285            $this->writeElementWithValAttribute($objWriter, 'c:showBubbleSize', '0');
1286            $this->writeElementWithValAttribute($objWriter, 'c:showLeaderLines', $series->hasShowLeaderLines() ? '1' : '0');
1287
1288            if ($series->hasDlblNumFormat()) {
1289                //c:numFmt
1290                $objWriter->startElement('c:numFmt');
1291                $objWriter->writeAttribute('formatCode', $series->getDlblNumFormat());
1292                $objWriter->writeAttribute('sourceLinked', '0');
1293                $objWriter->endElement();
1294            }
1295
1296            // c:dLbls\c:txPr
1297            $objWriter->startElement('c:txPr');
1298            $objWriter->writeElement('a:bodyPr', null);
1299            $objWriter->writeElement('a:lstStyle', null);
1300
1301            // c:dLbls\c:txPr\a:p
1302            $objWriter->startElement('a:p');
1303
1304            // c:dLbls\c:txPr\a:p\a:pPr
1305            $objWriter->startElement('a:pPr');
1306
1307            // c:dLbls\c:txPr\a:p\a:pPr\a:defRPr
1308            $objWriter->startElement('a:defRPr');
1309            $objWriter->writeAttribute('b', ($series->getFont()->isBold() ? 'true' : 'false'));
1310            $objWriter->writeAttribute('i', ($series->getFont()->isItalic() ? 'true' : 'false'));
1311            $objWriter->writeAttribute('strike', ($series->getFont()->isStrikethrough() ? 'sngStrike' : 'noStrike'));
1312            $objWriter->writeAttribute('sz', ($series->getFont()->getSize() * 100));
1313            $objWriter->writeAttribute('u', $series->getFont()->getUnderline());
1314            $objWriter->writeAttributeIf($series->getFont()->isSuperScript(), 'baseline', '300000');
1315            $objWriter->writeAttributeIf($series->getFont()->isSubScript(), 'baseline', '-250000');
1316
1317            // c:dLbls\c:txPr\a:p\a:pPr\a:defRPr\a:solidFill
1318            $objWriter->startElement('a:solidFill');
1319            $this->writeColor($objWriter, $series->getFont()->getColor());
1320            $objWriter->endElement();
1321
1322            // c:dLbls\c:txPr\a:p\a:pPr\a:defRPr\a:latin
1323            $objWriter->startElement('a:latin');
1324            $objWriter->writeAttribute('typeface', $series->getFont()->getName());
1325            $objWriter->endElement();
1326
1327            // c:dLbls\c:txPr\a:p\a:pPr\a:defRPr\
1328            $objWriter->endElement();
1329            // c:dLbls\c:txPr\a:p\a:pPr\
1330            $objWriter->endElement();
1331
1332            // c:dLbls\c:txPr\a:p\a:endParaRPr
1333            $objWriter->startElement('a:endParaRPr');
1334            $objWriter->writeAttribute('lang', 'en-US');
1335            $objWriter->writeAttribute('dirty', '0');
1336            $objWriter->endElement();
1337
1338            // c:dLbls\c:txPr\a:p\
1339            $objWriter->endElement();
1340            // c:dLbls\c:txPr\
1341            $objWriter->endElement();
1342
1343            $separator = $series->getSeparator();
1344            if (!empty($separator) && PHP_EOL != $separator) {
1345                // c:dLbls\c:separator
1346                $objWriter->writeElement('c:separator', $separator);
1347            }
1348
1349            // c:dLbls\
1350            $objWriter->endElement();
1351        }
1352
1353        $this->writeElementWithValAttribute($objWriter, 'c:firstSliceAng', '0');
1354        $this->writeElementWithValAttribute($objWriter, 'c:holeSize', (string) $subject->getHoleSize());
1355
1356        $objWriter->endElement();
1357    }
1358
1359    /**
1360     * Write Type Pie.
1361     *
1362     * @param XMLWriter $objWriter XML Writer
1363     *
1364     * @throws \Exception
1365     */
1366    protected function writeTypePie(XMLWriter $objWriter, Pie $subject, bool $includeSheet = false): void
1367    {
1368        // c:pieChart
1369        $objWriter->startElement('c:pieChart');
1370
1371        // c:varyColors
1372        $objWriter->startElement('c:varyColors');
1373        $objWriter->writeAttribute('val', '1');
1374        $objWriter->endElement();
1375
1376        // Write series
1377        $seriesIndex = 0;
1378        foreach ($subject->getSeries() as $series) {
1379            // c:ser
1380            $objWriter->startElement('c:ser');
1381
1382            // c:idx
1383            $objWriter->startElement('c:idx');
1384            $objWriter->writeAttribute('val', $seriesIndex);
1385            $objWriter->endElement();
1386
1387            // c:order
1388            $objWriter->startElement('c:order');
1389            $objWriter->writeAttribute('val', $seriesIndex);
1390            $objWriter->endElement();
1391
1392            // c:tx
1393            $objWriter->startElement('c:tx');
1394            $coords = ($includeSheet ? 'Sheet1!$' . Coordinate::stringFromColumnIndex(1 + $seriesIndex) . '$1' : '');
1395            $this->writeSingleValueOrReference($objWriter, $includeSheet, $series->getTitle(), $coords);
1396            $objWriter->endElement();
1397
1398            // Fills for points?
1399            $dataPointFills = $series->getDataPointFills();
1400            foreach ($dataPointFills as $key => $value) {
1401                // c:dPt
1402                $objWriter->startElement('c:dPt');
1403                $this->writeElementWithValAttribute($objWriter, 'c:idx', $key);
1404                // c:dPt/c:spPr
1405                $objWriter->startElement('c:spPr');
1406                $this->writeFill($objWriter, $value);
1407                // c:dPt/##c:spPr
1408                $objWriter->endElement();
1409                // ##c:dPt
1410                $objWriter->endElement();
1411            }
1412
1413            // c:dLbls
1414            $objWriter->startElement('c:dLbls');
1415
1416            if ($series->hasDlblNumFormat()) {
1417                //c:numFmt
1418                $objWriter->startElement('c:numFmt');
1419                $objWriter->writeAttribute('formatCode', $series->getDlblNumFormat());
1420                $objWriter->writeAttribute('sourceLinked', '0');
1421                $objWriter->endElement();
1422            }
1423
1424            // c:txPr
1425            $objWriter->startElement('c:txPr');
1426
1427            // a:bodyPr
1428            $objWriter->writeElement('a:bodyPr', null);
1429
1430            // a:lstStyle
1431            $objWriter->writeElement('a:lstStyle', null);
1432
1433            // a:p
1434            $objWriter->startElement('a:p');
1435
1436            // a:pPr
1437            $objWriter->startElement('a:pPr');
1438
1439            // a:defRPr
1440            $objWriter->startElement('a:defRPr');
1441
1442            $objWriter->writeAttribute('b', ($series->getFont()->isBold() ? 'true' : 'false'));
1443            $objWriter->writeAttribute('i', ($series->getFont()->isItalic() ? 'true' : 'false'));
1444            $objWriter->writeAttribute('strike', ($series->getFont()->isStrikethrough() ? 'sngStrike' : 'noStrike'));
1445            $objWriter->writeAttribute('sz', ($series->getFont()->getSize() * 100));
1446            $objWriter->writeAttribute('u', $series->getFont()->getUnderline());
1447            $objWriter->writeAttributeIf($series->getFont()->isSuperScript(), 'baseline', '300000');
1448            $objWriter->writeAttributeIf($series->getFont()->isSubScript(), 'baseline', '-250000');
1449
1450            // Font - a:solidFill
1451            $objWriter->startElement('a:solidFill');
1452
1453            $this->writeColor($objWriter, $series->getFont()->getColor());
1454
1455            $objWriter->endElement();
1456
1457            // Font - a:latin
1458            $objWriter->startElement('a:latin');
1459            $objWriter->writeAttribute('typeface', $series->getFont()->getName());
1460            $objWriter->endElement();
1461
1462            $objWriter->endElement();
1463
1464            $objWriter->endElement();
1465
1466            // a:endParaRPr
1467            $objWriter->startElement('a:endParaRPr');
1468            $objWriter->writeAttribute('lang', 'en-US');
1469            $objWriter->writeAttribute('dirty', '0');
1470            $objWriter->endElement();
1471
1472            $objWriter->endElement();
1473
1474            $objWriter->endElement();
1475
1476            // c:dLblPos
1477            $this->writeElementWithValAttribute($objWriter, 'c:dLblPos', $series->getLabelPosition());
1478
1479            // c:showLegendKey
1480            $this->writeElementWithValAttribute($objWriter, 'c:showLegendKey', $series->hasShowLegendKey() ? '1' : '0');
1481
1482            // c:showVal
1483            $this->writeElementWithValAttribute($objWriter, 'c:showVal', $series->hasShowValue() ? '1' : '0');
1484
1485            // c:showCatName
1486            $this->writeElementWithValAttribute($objWriter, 'c:showCatName', $series->hasShowCategoryName() ? '1' : '0');
1487
1488            // c:showSerName
1489            $this->writeElementWithValAttribute($objWriter, 'c:showSerName', $series->hasShowSeriesName() ? '1' : '0');
1490
1491            // c:showPercent
1492            $this->writeElementWithValAttribute($objWriter, 'c:showPercent', $series->hasShowPercentage() ? '1' : '0');
1493
1494            // c:showLeaderLines
1495            $this->writeElementWithValAttribute($objWriter, 'c:showLeaderLines', $series->hasShowLeaderLines() ? '1' : '0');
1496
1497            $objWriter->endElement();
1498
1499            // Write X axis data
1500            $axisXData = array_keys($series->getValues());
1501
1502            // c:cat
1503            $objWriter->startElement('c:cat');
1504            $this->writeMultipleValuesOrReference($objWriter, $includeSheet, $axisXData, 'Sheet1!$A$2:$A$' . (1 + count($axisXData)));
1505            $objWriter->endElement();
1506
1507            // Write Y axis data
1508            $axisYData = array_values($series->getValues());
1509
1510            // c:val
1511            $objWriter->startElement('c:val');
1512            $coords = ($includeSheet ? 'Sheet1!$' . Coordinate::stringFromColumnIndex($seriesIndex + 1) . '$2:$' . Coordinate::stringFromColumnIndex($seriesIndex + 1) . '$' . (1 + count($axisYData)) : '');
1513            $this->writeMultipleValuesOrReference($objWriter, $includeSheet, $axisYData, $coords);
1514            $objWriter->endElement();
1515
1516            $objWriter->endElement();
1517
1518            ++$seriesIndex;
1519        }
1520
1521        $objWriter->endElement();
1522    }
1523
1524    /**
1525     * Write Type Pie3D.
1526     *
1527     * @param XMLWriter $objWriter XML Writer
1528     *
1529     * @throws \Exception
1530     */
1531    protected function writeTypePie3D(XMLWriter $objWriter, Pie3D $subject, bool $includeSheet = false): void
1532    {
1533        // c:pie3DChart
1534        $objWriter->startElement('c:pie3DChart');
1535
1536        // c:varyColors
1537        $objWriter->startElement('c:varyColors');
1538        $objWriter->writeAttribute('val', '1');
1539        $objWriter->endElement();
1540
1541        // Write series
1542        $seriesIndex = 0;
1543        foreach ($subject->getSeries() as $series) {
1544            // c:ser
1545            $objWriter->startElement('c:ser');
1546
1547            // c:idx
1548            $objWriter->startElement('c:idx');
1549            $objWriter->writeAttribute('val', $seriesIndex);
1550            $objWriter->endElement();
1551
1552            // c:order
1553            $objWriter->startElement('c:order');
1554            $objWriter->writeAttribute('val', $seriesIndex);
1555            $objWriter->endElement();
1556
1557            // c:tx
1558            $objWriter->startElement('c:tx');
1559            $coords = ($includeSheet ? 'Sheet1!$' . Coordinate::stringFromColumnIndex(1 + $seriesIndex) . '$1' : '');
1560            $this->writeSingleValueOrReference($objWriter, $includeSheet, $series->getTitle(), $coords);
1561            $objWriter->endElement();
1562
1563            // c:explosion
1564            $objWriter->startElement('c:explosion');
1565            $objWriter->writeAttribute('val', $subject->getExplosion());
1566            $objWriter->endElement();
1567
1568            // Fills for points?
1569            $dataPointFills = $series->getDataPointFills();
1570            foreach ($dataPointFills as $key => $value) {
1571                // c:dPt
1572                $objWriter->startElement('c:dPt');
1573                $this->writeElementWithValAttribute($objWriter, 'c:idx', $key);
1574                // c:dPt/c:spPr
1575                $objWriter->startElement('c:spPr');
1576                $this->writeFill($objWriter, $value);
1577                // c:dPt/##c:spPr
1578                $objWriter->endElement();
1579                // ##c:dPt
1580                $objWriter->endElement();
1581            }
1582
1583            // c:dLbls
1584            $objWriter->startElement('c:dLbls');
1585
1586            // c:txPr
1587            $objWriter->startElement('c:txPr');
1588
1589            // a:bodyPr
1590            $objWriter->writeElement('a:bodyPr', null);
1591
1592            // a:lstStyle
1593            $objWriter->writeElement('a:lstStyle', null);
1594
1595            // a:p
1596            $objWriter->startElement('a:p');
1597
1598            // a:pPr
1599            $objWriter->startElement('a:pPr');
1600
1601            // a:defRPr
1602            $objWriter->startElement('a:defRPr');
1603
1604            $objWriter->writeAttribute('b', ($series->getFont()->isBold() ? 'true' : 'false'));
1605            $objWriter->writeAttribute('i', ($series->getFont()->isItalic() ? 'true' : 'false'));
1606            $objWriter->writeAttribute('strike', ($series->getFont()->isStrikethrough() ? 'sngStrike' : 'noStrike'));
1607            $objWriter->writeAttribute('sz', ($series->getFont()->getSize() * 100));
1608            $objWriter->writeAttribute('u', $series->getFont()->getUnderline());
1609            $objWriter->writeAttributeIf($series->getFont()->isSuperScript(), 'baseline', '300000');
1610            $objWriter->writeAttributeIf($series->getFont()->isSubScript(), 'baseline', '-250000');
1611
1612            // Font - a:solidFill
1613            $objWriter->startElement('a:solidFill');
1614
1615            $this->writeColor($objWriter, $series->getFont()->getColor());
1616
1617            $objWriter->endElement();
1618
1619            // Font - a:latin
1620            $objWriter->startElement('a:latin');
1621            $objWriter->writeAttribute('typeface', $series->getFont()->getName());
1622            $objWriter->endElement();
1623
1624            $objWriter->endElement();
1625
1626            $objWriter->endElement();
1627
1628            // a:endParaRPr
1629            $objWriter->startElement('a:endParaRPr');
1630            $objWriter->writeAttribute('lang', 'en-US');
1631            $objWriter->writeAttribute('dirty', '0');
1632            $objWriter->endElement();
1633
1634            $objWriter->endElement();
1635
1636            $objWriter->endElement();
1637
1638            // c:dLblPos
1639            $this->writeElementWithValAttribute($objWriter, 'c:dLblPos', $series->getLabelPosition());
1640
1641            // c:showVal
1642            $this->writeElementWithValAttribute($objWriter, 'c:showVal', $series->hasShowValue() ? '1' : '0');
1643
1644            // c:showCatName
1645            $this->writeElementWithValAttribute($objWriter, 'c:showCatName', $series->hasShowCategoryName() ? '1' : '0');
1646
1647            // c:showSerName
1648            $this->writeElementWithValAttribute($objWriter, 'c:showSerName', $series->hasShowSeriesName() ? '1' : '0');
1649
1650            // c:showPercent
1651            $this->writeElementWithValAttribute($objWriter, 'c:showPercent', $series->hasShowPercentage() ? '1' : '0');
1652
1653            // c:showLeaderLines
1654            $this->writeElementWithValAttribute($objWriter, 'c:showLeaderLines', $series->hasShowLeaderLines() ? '1' : '0');
1655
1656            $objWriter->endElement();
1657
1658            // Write X axis data
1659            $axisXData = array_keys($series->getValues());
1660
1661            // c:cat
1662            $objWriter->startElement('c:cat');
1663            $this->writeMultipleValuesOrReference($objWriter, $includeSheet, $axisXData, 'Sheet1!$A$2:$A$' . (1 + count($axisXData)));
1664            $objWriter->endElement();
1665
1666            // Write Y axis data
1667            $axisYData = array_values($series->getValues());
1668
1669            // c:val
1670            $objWriter->startElement('c:val');
1671            $coords = ($includeSheet ? 'Sheet1!$' . Coordinate::stringFromColumnIndex($seriesIndex + 1) . '$2:$' . Coordinate::stringFromColumnIndex($seriesIndex + 1) . '$' . (1 + count($axisYData)) : '');
1672            $this->writeMultipleValuesOrReference($objWriter, $includeSheet, $axisYData, $coords);
1673            $objWriter->endElement();
1674
1675            $objWriter->endElement();
1676
1677            ++$seriesIndex;
1678        }
1679
1680        $objWriter->endElement();
1681    }
1682
1683    /**
1684     * Write Type Line.
1685     *
1686     * @param XMLWriter $objWriter XML Writer
1687     *
1688     * @throws \Exception
1689     */
1690    protected function writeTypeLine(XMLWriter $objWriter, Line $subject, bool $includeSheet = false): void
1691    {
1692        // c:lineChart
1693        $objWriter->startElement('c:lineChart');
1694
1695        // c:grouping
1696        $objWriter->startElement('c:grouping');
1697        $objWriter->writeAttribute('val', 'standard');
1698        $objWriter->endElement();
1699
1700        // Write series
1701        $seriesIndex = 0;
1702        foreach ($subject->getSeries() as $series) {
1703            // c:ser
1704            $objWriter->startElement('c:ser');
1705
1706            // c:idx
1707            $objWriter->startElement('c:idx');
1708            $objWriter->writeAttribute('val', $seriesIndex);
1709            $objWriter->endElement();
1710
1711            // c:order
1712            $objWriter->startElement('c:order');
1713            $objWriter->writeAttribute('val', $seriesIndex);
1714            $objWriter->endElement();
1715
1716            // c:tx
1717            $objWriter->startElement('c:tx');
1718            $coords = ($includeSheet ? 'Sheet1!$' . Coordinate::stringFromColumnIndex(1 + $seriesIndex) . '$1' : '');
1719            $this->writeSingleValueOrReference($objWriter, $includeSheet, $series->getTitle(), $coords);
1720            $objWriter->endElement();
1721
1722            // c:spPr
1723            $objWriter->startElement('c:spPr');
1724            // Write fill
1725            $this->writeFill($objWriter, $series->getFill());
1726            // Write outline
1727            $this->writeOutline($objWriter, $series->getOutline());
1728            // ## c:spPr
1729            $objWriter->endElement();
1730
1731            // Marker
1732            $this->writeSeriesMarker($objWriter, $series->getMarker());
1733
1734            // c:dLbls
1735            $objWriter->startElement('c:dLbls');
1736
1737            // c:txPr
1738            $objWriter->startElement('c:txPr');
1739
1740            // a:bodyPr
1741            $objWriter->writeElement('a:bodyPr', null);
1742
1743            // a:lstStyle
1744            $objWriter->writeElement('a:lstStyle', null);
1745
1746            // a:p
1747            $objWriter->startElement('a:p');
1748
1749            // a:pPr
1750            $objWriter->startElement('a:pPr');
1751
1752            // a:defRPr
1753            $objWriter->startElement('a:defRPr');
1754
1755            $objWriter->writeAttribute('b', ($series->getFont()->isBold() ? 'true' : 'false'));
1756            $objWriter->writeAttribute('i', ($series->getFont()->isItalic() ? 'true' : 'false'));
1757            $objWriter->writeAttribute('strike', ($series->getFont()->isStrikethrough() ? 'sngStrike' : 'noStrike'));
1758            $objWriter->writeAttribute('sz', ($series->getFont()->getSize() * 100));
1759            $objWriter->writeAttribute('u', $series->getFont()->getUnderline());
1760            $objWriter->writeAttributeIf($series->getFont()->isSuperScript(), 'baseline', '300000');
1761            $objWriter->writeAttributeIf($series->getFont()->isSubScript(), 'baseline', '-250000');
1762
1763            // Font - a:solidFill
1764            $objWriter->startElement('a:solidFill');
1765
1766            $this->writeColor($objWriter, $series->getFont()->getColor());
1767
1768            $objWriter->endElement();
1769
1770            // Font - a:latin
1771            $objWriter->startElement('a:latin');
1772            $objWriter->writeAttribute('typeface', $series->getFont()->getName());
1773            $objWriter->endElement();
1774
1775            $objWriter->endElement();
1776
1777            $objWriter->endElement();
1778
1779            // a:endParaRPr
1780            $objWriter->startElement('a:endParaRPr');
1781            $objWriter->writeAttribute('lang', 'en-US');
1782            $objWriter->writeAttribute('dirty', '0');
1783            $objWriter->endElement();
1784
1785            $objWriter->endElement();
1786
1787            $objWriter->endElement();
1788
1789            // c:showVal
1790            $this->writeElementWithValAttribute($objWriter, 'c:showVal', $series->hasShowValue() ? '1' : '0');
1791
1792            // c:showCatName
1793            $this->writeElementWithValAttribute($objWriter, 'c:showCatName', $series->hasShowCategoryName() ? '1' : '0');
1794
1795            // c:showSerName
1796            $this->writeElementWithValAttribute($objWriter, 'c:showSerName', $series->hasShowSeriesName() ? '1' : '0');
1797
1798            // c:showPercent
1799            $this->writeElementWithValAttribute($objWriter, 'c:showPercent', $series->hasShowPercentage() ? '1' : '0');
1800
1801            // c:showLeaderLines
1802            $this->writeElementWithValAttribute($objWriter, 'c:showLeaderLines', $series->hasShowLeaderLines() ? '1' : '0');
1803
1804            // > c:dLbls
1805            $objWriter->endElement();
1806
1807            // Write X axis data
1808            $axisXData = array_keys($series->getValues());
1809
1810            // c:cat
1811            $objWriter->startElement('c:cat');
1812            $this->writeMultipleValuesOrReference($objWriter, $includeSheet, $axisXData, 'Sheet1!$A$2:$A$' . (1 + count($axisXData)));
1813            $objWriter->endElement();
1814
1815            // Write Y axis data
1816            $axisYData = array_values($series->getValues());
1817
1818            // c:val
1819            $objWriter->startElement('c:val');
1820            $coords = ($includeSheet ? 'Sheet1!$' . Coordinate::stringFromColumnIndex($seriesIndex + 1) . '$2:$' . Coordinate::stringFromColumnIndex($seriesIndex + 1) . '$' . (1 + count($axisYData)) : '');
1821            $this->writeMultipleValuesOrReference($objWriter, $includeSheet, $axisYData, $coords);
1822            $objWriter->endElement();
1823
1824            $objWriter->endElement();
1825
1826            ++$seriesIndex;
1827        }
1828
1829        // c:marker
1830        $objWriter->startElement('c:marker');
1831        $objWriter->writeAttribute('val', '1');
1832        $objWriter->endElement();
1833
1834        // c:smooth
1835        $objWriter->startElement('c:smooth');
1836        $objWriter->writeAttribute('val', '0');
1837        $objWriter->endElement();
1838
1839        // c:axId
1840        $objWriter->startElement('c:axId');
1841        $objWriter->writeAttribute('val', '52743552');
1842        $objWriter->endElement();
1843
1844        // c:axId
1845        $objWriter->startElement('c:axId');
1846        $objWriter->writeAttribute('val', '52749440');
1847        $objWriter->endElement();
1848
1849        $objWriter->endElement();
1850    }
1851
1852    /**
1853     * Write Type Scatter.
1854     *
1855     * @param XMLWriter $objWriter XML Writer
1856     * @param Chart\Type\Scatter $subject
1857     *
1858     * @throws \Exception
1859     */
1860    protected function writeTypeScatter(XMLWriter $objWriter, Scatter $subject, bool $includeSheet = false): void
1861    {
1862        // c:scatterChart
1863        $objWriter->startElement('c:scatterChart');
1864
1865        // c:scatterStyle
1866        $objWriter->startElement('c:scatterStyle');
1867        $objWriter->writeAttribute('val', 'lineMarker');
1868        $objWriter->endElement();
1869
1870        // c:varyColors
1871        $objWriter->startElement('c:varyColors');
1872        $objWriter->writeAttribute('val', '0');
1873        $objWriter->endElement();
1874
1875        // Write series
1876        $seriesIndex = 0;
1877        foreach ($subject->getSeries() as $series) {
1878            // c:ser
1879            $objWriter->startElement('c:ser');
1880
1881            // c:idx
1882            $objWriter->startElement('c:idx');
1883            $objWriter->writeAttribute('val', $seriesIndex);
1884            $objWriter->endElement();
1885
1886            // c:order
1887            $objWriter->startElement('c:order');
1888            $objWriter->writeAttribute('val', $seriesIndex);
1889            $objWriter->endElement();
1890
1891            // c:tx
1892            $objWriter->startElement('c:tx');
1893            $coords = ($includeSheet ? 'Sheet1!$' . Coordinate::stringFromColumnIndex(1 + $seriesIndex) . '$1' : '');
1894            $this->writeSingleValueOrReference($objWriter, $includeSheet, $series->getTitle(), $coords);
1895            $objWriter->endElement();
1896
1897            // c:spPr
1898            $objWriter->startElement('c:spPr');
1899            // Write fill
1900            $this->writeFill($objWriter, $series->getFill());
1901            // Write outline
1902            $this->writeOutline($objWriter, $series->getOutline());
1903            // ## c:spPr
1904            $objWriter->endElement();
1905
1906            // Marker
1907            $this->writeSeriesMarker($objWriter, $series->getMarker());
1908
1909            // c:dLbls
1910            $objWriter->startElement('c:dLbls');
1911
1912            // c:txPr
1913            $objWriter->startElement('c:txPr');
1914
1915            // a:bodyPr
1916            $objWriter->writeElement('a:bodyPr', null);
1917
1918            // a:lstStyle
1919            $objWriter->writeElement('a:lstStyle', null);
1920
1921            // a:p
1922            $objWriter->startElement('a:p');
1923
1924            // a:pPr
1925            $objWriter->startElement('a:pPr');
1926
1927            // a:defRPr
1928            $objWriter->startElement('a:defRPr');
1929
1930            $objWriter->writeAttribute('b', ($series->getFont()->isBold() ? 'true' : 'false'));
1931            $objWriter->writeAttribute('i', ($series->getFont()->isItalic() ? 'true' : 'false'));
1932            $objWriter->writeAttribute('strike', ($series->getFont()->isStrikethrough() ? 'sngStrike' : 'noStrike'));
1933            $objWriter->writeAttribute('sz', ($series->getFont()->getSize() * 100));
1934            $objWriter->writeAttribute('u', $series->getFont()->getUnderline());
1935            $objWriter->writeAttributeIf($series->getFont()->isSuperScript(), 'baseline', '300000');
1936            $objWriter->writeAttributeIf($series->getFont()->isSubScript(), 'baseline', '-250000');
1937
1938            // Font - a:solidFill
1939            $objWriter->startElement('a:solidFill');
1940
1941            $this->writeColor($objWriter, $series->getFont()->getColor());
1942
1943            $objWriter->endElement();
1944
1945            // Font - a:latin
1946            $objWriter->startElement('a:latin');
1947            $objWriter->writeAttribute('typeface', $series->getFont()->getName());
1948            $objWriter->endElement();
1949
1950            $objWriter->endElement();
1951
1952            $objWriter->endElement();
1953
1954            // a:endParaRPr
1955            $objWriter->startElement('a:endParaRPr');
1956            $objWriter->writeAttribute('lang', 'en-US');
1957            $objWriter->writeAttribute('dirty', '0');
1958            $objWriter->endElement();
1959
1960            $objWriter->endElement();
1961
1962            $objWriter->endElement();
1963
1964            // c:showLegendKey
1965            $this->writeElementWithValAttribute($objWriter, 'c:showLegendKey', $series->hasShowLegendKey() ? '1' : '0');
1966
1967            // c:showVal
1968            $this->writeElementWithValAttribute($objWriter, 'c:showVal', $series->hasShowValue() ? '1' : '0');
1969
1970            // c:showCatName
1971            $this->writeElementWithValAttribute($objWriter, 'c:showCatName', $series->hasShowCategoryName() ? '1' : '0');
1972
1973            // c:showSerName
1974            $this->writeElementWithValAttribute($objWriter, 'c:showSerName', $series->hasShowSeriesName() ? '1' : '0');
1975
1976            // c:showPercent
1977            $this->writeElementWithValAttribute($objWriter, 'c:showPercent', $series->hasShowPercentage() ? '1' : '0');
1978
1979            // c:separator
1980            $separator = $series->getSeparator();
1981            if (!empty($separator) && PHP_EOL != $separator) {
1982                // c:dLbls\c:separator
1983                $objWriter->writeElement('c:separator', $separator);
1984            }
1985
1986            // c:showLeaderLines
1987            $this->writeElementWithValAttribute($objWriter, 'c:showLeaderLines', $series->hasShowLeaderLines() ? '1' : '0');
1988
1989            $objWriter->endElement();
1990
1991            // Write X axis data
1992            $axisXData = array_keys($series->getValues());
1993
1994            // c:xVal
1995            $objWriter->startElement('c:xVal');
1996            $this->writeMultipleValuesOrReference($objWriter, $includeSheet, $axisXData, 'Sheet1!$A$2:$A$' . (1 + count($axisXData)));
1997            $objWriter->endElement();
1998
1999            // Write Y axis data
2000            $axisYData = array_values($series->getValues());
2001
2002            // c:yVal
2003            $objWriter->startElement('c:yVal');
2004            $coords = ($includeSheet ? 'Sheet1!$' . Coordinate::stringFromColumnIndex($seriesIndex + 1) . '$2:$' . Coordinate::stringFromColumnIndex($seriesIndex + 1) . '$' . (1 + count($axisYData)) : '');
2005            $this->writeMultipleValuesOrReference($objWriter, $includeSheet, $axisYData, $coords);
2006            $objWriter->endElement();
2007
2008            // c:smooth
2009            $objWriter->startElement('c:smooth');
2010            $objWriter->writeAttribute('val', '0');
2011            $objWriter->endElement();
2012
2013            $objWriter->endElement();
2014
2015            ++$seriesIndex;
2016        }
2017
2018        // c:axId
2019        $objWriter->startElement('c:axId');
2020        $objWriter->writeAttribute('val', '52743552');
2021        $objWriter->endElement();
2022
2023        // c:axId
2024        $objWriter->startElement('c:axId');
2025        $objWriter->writeAttribute('val', '52749440');
2026        $objWriter->endElement();
2027
2028        $objWriter->endElement();
2029    }
2030
2031    /**
2032     * Write chart relationships to XML format.
2033     *
2034     * @return string XML Output
2035     *
2036     * @throws \Exception
2037     */
2038    public function writeChartRelationships(Chart $pChart): string
2039    {
2040        // Create XML writer
2041        $objWriter = new XMLWriter(XMLWriter::STORAGE_MEMORY);
2042
2043        // XML header
2044        $objWriter->startDocument('1.0', 'UTF-8', 'yes');
2045
2046        // Relationships
2047        $objWriter->startElement('Relationships');
2048        $objWriter->writeAttribute('xmlns', 'http://schemas.openxmlformats.org/package/2006/relationships');
2049
2050        // Write spreadsheet relationship?
2051        if ($pChart->hasIncludedSpreadsheet()) {
2052            $this->writeRelationship($objWriter, 1, 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/package', '../embeddings/' . $pChart->getIndexedFilename() . '.xlsx');
2053        }
2054
2055        $objWriter->endElement();
2056
2057        // Return
2058        return $objWriter->getData();
2059    }
2060
2061    protected function writeSeriesMarker(XMLWriter $objWriter, Chart\Marker $marker): void
2062    {
2063        // c:marker
2064        $objWriter->startElement('c:marker');
2065        // c:marker > c:symbol
2066        $objWriter->startElement('c:symbol');
2067        $objWriter->writeAttribute('val', $marker->getSymbol());
2068        $objWriter->endElement();
2069
2070        // Size if different of none
2071        if (Chart\Marker::SYMBOL_NONE != $marker->getSymbol()) {
2072            $markerSize = (int) $marker->getSize();
2073            if ($markerSize < 2) {
2074                $markerSize = 2;
2075            }
2076            if ($markerSize > 72) {
2077                $markerSize = 72;
2078            }
2079
2080            /*
2081             * c:marker > c:size
2082             * Size in points
2083             * @link : https://msdn.microsoft.com/en-us/library/hh658135(v=office.12).aspx
2084             */
2085            $objWriter->startElement('c:size');
2086            $objWriter->writeAttribute('val', $markerSize);
2087            $objWriter->endElement();
2088        }
2089
2090        // // c:marker > c:spPr
2091        $objWriter->startElement('c:spPr');
2092        $this->writeFill($objWriter, $marker->getFill());
2093        $this->writeBorder($objWriter, $marker->getBorder(), '', true);
2094        $objWriter->endElement();
2095
2096        // > c:marker
2097        $objWriter->endElement();
2098    }
2099
2100    /**
2101     * @throws \Exception
2102     */
2103    protected function writeAxis(XMLWriter $objWriter, Chart\Axis $oAxis, string $typeAxis, Chart\Type\AbstractType $typeChart): void
2104    {
2105        if (Chart\Axis::AXIS_X != $typeAxis && Chart\Axis::AXIS_Y != $typeAxis) {
2106            return;
2107        }
2108
2109        if (Chart\Axis::AXIS_X == $typeAxis) {
2110            $mainElement = 'c:catAx';
2111            $axIdVal = '52743552';
2112            $axPosVal = 'b';
2113            $crossAxVal = '52749440';
2114        } else {
2115            $mainElement = 'c:valAx';
2116            $axIdVal = '52749440';
2117            $axPosVal = 'l';
2118            $crossAxVal = '52743552';
2119        }
2120
2121        // $mainElement
2122        $objWriter->startElement($mainElement);
2123
2124        // $mainElement > c:axId
2125        $objWriter->startElement('c:axId');
2126        $objWriter->writeAttribute('val', $axIdVal);
2127        $objWriter->endElement();
2128
2129        // $mainElement > c:scaling
2130        $objWriter->startElement('c:scaling');
2131
2132        // $mainElement > c:scaling > c:orientation
2133        $objWriter->startElement('c:orientation');
2134        $objWriter->writeAttribute('val', 'minMax');
2135        $objWriter->endElement();
2136
2137        if (null != $oAxis->getMaxBounds()) {
2138            $objWriter->startElement('c:max');
2139            $objWriter->writeAttribute('val', $oAxis->getMaxBounds());
2140            $objWriter->endElement();
2141        }
2142
2143        if (null != $oAxis->getMinBounds()) {
2144            $objWriter->startElement('c:min');
2145            $objWriter->writeAttribute('val', $oAxis->getMinBounds());
2146            $objWriter->endElement();
2147        }
2148
2149        // $mainElement > ##c:scaling
2150        $objWriter->endElement();
2151
2152        // $mainElement > c:delete
2153        $objWriter->startElement('c:delete');
2154        $objWriter->writeAttribute('val', $oAxis->isVisible() ? '0' : '1');
2155        $objWriter->endElement();
2156
2157        // $mainElement > c:axPos
2158        $objWriter->startElement('c:axPos');
2159        $objWriter->writeAttribute('val', $axPosVal);
2160        $objWriter->endElement();
2161
2162        $oMajorGridlines = $oAxis->getMajorGridlines();
2163        if ($oMajorGridlines instanceof Gridlines) {
2164            $objWriter->startElement('c:majorGridlines');
2165
2166            $this->writeAxisGridlines($objWriter, $oMajorGridlines);
2167
2168            $objWriter->endElement();
2169        }
2170
2171        $oMinorGridlines = $oAxis->getMinorGridlines();
2172        if ($oMinorGridlines instanceof Gridlines) {
2173            $objWriter->startElement('c:minorGridlines');
2174
2175            $this->writeAxisGridlines($objWriter, $oMinorGridlines);
2176
2177            $objWriter->endElement();
2178        }
2179
2180        if ('' != $oAxis->getTitle()) {
2181            // c:title
2182            $objWriter->startElement('c:title');
2183
2184            // c:tx
2185            $objWriter->startElement('c:tx');
2186
2187            // c:rich
2188            $objWriter->startElement('c:rich');
2189
2190            // a:bodyPr
2191            $objWriter->startElement('a:bodyPr');
2192            $objWriter->writeAttributeIf($oAxis->getTitleRotation() != 0, 'rot', CommonDrawing::degreesToAngle($oAxis->getTitleRotation()));
2193            $objWriter->endElement();
2194
2195            // a:lstStyle
2196            $objWriter->writeElement('a:lstStyle', null);
2197
2198            // a:p
2199            $objWriter->startElement('a:p');
2200
2201            // a:pPr
2202            $objWriter->startElement('a:pPr');
2203
2204            // a:defRPr
2205            $objWriter->startElement('a:defRPr');
2206
2207            $objWriter->writeAttribute('b', ($oAxis->getFont()->isBold() ? 'true' : 'false'));
2208            $objWriter->writeAttribute('i', ($oAxis->getFont()->isItalic() ? 'true' : 'false'));
2209            $objWriter->writeAttribute('strike', ($oAxis->getFont()->isStrikethrough() ? 'sngStrike' : 'noStrike'));
2210            $objWriter->writeAttribute('sz', ($oAxis->getFont()->getSize() * 100));
2211            $objWriter->writeAttribute('u', $oAxis->getFont()->getUnderline());
2212            $objWriter->writeAttributeIf($oAxis->getFont()->isSuperScript(), 'baseline', '300000');
2213            $objWriter->writeAttributeIf($oAxis->getFont()->isSubScript(), 'baseline', '-250000');
2214
2215            // Font - a:solidFill
2216            $objWriter->startElement('a:solidFill');
2217            $this->writeColor($objWriter, $oAxis->getFont()->getColor());
2218            $objWriter->endElement();
2219
2220            // Font - a:latin
2221            $objWriter->startElement('a:latin');
2222            $objWriter->writeAttribute('typeface', $oAxis->getFont()->getName());
2223            $objWriter->endElement();
2224
2225            $objWriter->endElement();
2226
2227            // ## a:pPr
2228            $objWriter->endElement();
2229
2230            // a:r
2231            $objWriter->startElement('a:r');
2232
2233            // a:rPr
2234            $objWriter->startElement('a:rPr');
2235            $objWriter->writeAttribute('lang', 'en-US');
2236            $objWriter->writeAttribute('dirty', '0');
2237            $objWriter->endElement();
2238
2239            // a:t
2240            $objWriter->writeElement('a:t', $oAxis->getTitle());
2241
2242            // ## a:r
2243            $objWriter->endElement();
2244
2245            // a:endParaRPr
2246            $objWriter->startElement('a:endParaRPr');
2247            $objWriter->writeAttribute('lang', 'en-US');
2248            $objWriter->writeAttribute('dirty', '0');
2249            $objWriter->endElement();
2250
2251            // ## a:p
2252            $objWriter->endElement();
2253
2254            // ## c:rich
2255            $objWriter->endElement();
2256
2257            // ## c:tx
2258            $objWriter->endElement();
2259
2260            // ## c:title
2261            $objWriter->endElement();
2262        }
2263
2264        // c:numFmt
2265        $objWriter->startElement('c:numFmt');
2266        $objWriter->writeAttribute('formatCode', $oAxis->getFormatCode());
2267        $objWriter->writeAttribute('sourceLinked', '1');
2268        $objWriter->endElement();
2269
2270        // c:majorTickMark
2271        $objWriter->startElement('c:majorTickMark');
2272        $objWriter->writeAttribute('val', $oAxis->getMajorTickMark());
2273        $objWriter->endElement();
2274
2275        // c:minorTickMark
2276        $objWriter->startElement('c:minorTickMark');
2277        $objWriter->writeAttribute('val', $oAxis->getMinorTickMark());
2278        $objWriter->endElement();
2279
2280        // c:tickLblPos
2281        $objWriter->startElement('c:tickLblPos');
2282        $objWriter->writeAttribute('val', 'nextTo');
2283        $objWriter->endElement();
2284
2285        // c:spPr
2286        $objWriter->startElement('c:spPr');
2287        // Outline
2288        $this->writeOutline($objWriter, $oAxis->getOutline());
2289        // ##c:spPr
2290        $objWriter->endElement();
2291
2292        // c:crossAx
2293        $objWriter->startElement('c:crossAx');
2294        $objWriter->writeAttribute('val', $crossAxVal);
2295        $objWriter->endElement();
2296
2297        // c:crosses
2298        $objWriter->startElement('c:crosses');
2299        $objWriter->writeAttribute('val', 'autoZero');
2300        $objWriter->endElement();
2301
2302        if (Chart\Axis::AXIS_X == $typeAxis) {
2303            // c:lblAlgn
2304            $objWriter->startElement('c:lblAlgn');
2305            $objWriter->writeAttribute('val', 'ctr');
2306            $objWriter->endElement();
2307
2308            // c:lblOffset
2309            $objWriter->startElement('c:lblOffset');
2310            $objWriter->writeAttribute('val', '100');
2311            $objWriter->endElement();
2312        }
2313
2314        if (Chart\Axis::AXIS_Y == $typeAxis) {
2315            // c:crossBetween
2316            $objWriter->startElement('c:crossBetween');
2317            // midCat : Position Axis On Tick Marks
2318            // between : Between Tick Marks
2319            if ($typeChart instanceof Area) {
2320                $objWriter->writeAttribute('val', 'midCat');
2321            } else {
2322                $objWriter->writeAttribute('val', 'between');
2323            }
2324            $objWriter->endElement();
2325
2326            // c:majorUnit
2327            if (null != $oAxis->getMajorUnit()) {
2328                $objWriter->startElement('c:majorUnit');
2329                $objWriter->writeAttribute('val', $oAxis->getMajorUnit());
2330                $objWriter->endElement();
2331            }
2332
2333            // c:minorUnit
2334            if (null != $oAxis->getMinorUnit()) {
2335                $objWriter->startElement('c:minorUnit');
2336                $objWriter->writeAttribute('val', $oAxis->getMinorUnit());
2337                $objWriter->endElement();
2338            }
2339        }
2340
2341        $objWriter->endElement();
2342    }
2343
2344    /**
2345     * @throws \Exception
2346     */
2347    protected function writeAxisGridlines(XMLWriter $objWriter, Gridlines $oGridlines): void
2348    {
2349        // c:spPr
2350        $objWriter->startElement('c:spPr');
2351
2352        // Outline
2353        $this->writeOutline($objWriter, $oGridlines->getOutline());
2354
2355        // ##c:spPr
2356        $objWriter->endElement();
2357    }
2358}